//
// Created by YxMin on 2024/7/9.
//
#include "../ui/element/Map.h"
#include "UI.h"
#include "tinycthread.h"
#include <procedure.h>
#include <network.h>
#include <netdata.h>
#include <map>
mtx_t ProjectileCalcLocker;
std::map<int,Projectile> Projectiles[4];
Map map(GAME_UI,33,33,833,633,5);
Player ServerPlayers[4];
#define PLAYER_POSITION_UPDATE 1
#define PROJECTILE_CRASH 2
#define PLAYER_ACCUMULATE_POWER 3
#define PROJECTILE_SHOOT 4
#define PROJECTILE_DURABILITY_UPDATE 5
#define PLAYER_AMMUNITION_UPDATE 6
#define PLAYER_HEALTH_UPDATE 7
#define BLOCK_HEALTH_UPDATE 8
#define PLAYER_BASE_HEALTH_UPDATE 9
#define GAME_OVER 10
#define PLAYER_DISCONNECT 11
extern bool ServerOn;
void handlePlayerPositionUpdatePack(GamePack &pack,int x,int y,int direction,char* buffer);
int calcProjectiles(void* arg);
int ProjectileTick = 0;

void ServerStartGame() {
    map.loadFromFile("map/1.txt");
    for (int i = 0; i < 4; ++i) {
        if (!IsClientOnline(i)) {
            ServerPlayers[i].id = -1; //表示该玩家离线
            continue;
        }
        ServerPlayers[i].id = i;
        PixelXY xy;
        switch (i) {
            case 0:
                xy = map.getPixelXYByPosition(0,0);
                ServerPlayers[i].direction = 3;
                break;
            case 1:
                xy = map.getPixelXYByPosition(0,19);
                ServerPlayers[i].direction = 3;
                break;
            case 2:
                xy = map.getPixelXYByPosition(14,0);
                ServerPlayers[i].direction = 1;
                break;
            case 3:
                xy = map.getPixelXYByPosition(14,19);
                ServerPlayers[i].direction = 1;
                break;
        }
        ServerPlayers[i].x = xy.x;
        ServerPlayers[i].y = xy.y;
        ServerPlayers[i].health = 3;
        ServerPlayers[i].baseHealth = 10;
        ServerPlayers[i].accumulateTime = 0;
        ServerPlayers[i].ammunition = 5;
    }
    StopBroadcast();
    RegisterClientMessageHandler(ParseAndHandleGamePackFromPlayer);
    SendGameStartPack();
    int waitStart = 0;
    while(!IsAllPlayerReadyToStart()) {
        waitStart++;
        if (waitStart > 1000) {
            printf("我已经等了一千毫秒了还有玩家没准备好，服了\n");
            break;
        }
        Sleep(1);
    }
    mtx_init(&ProjectileCalcLocker,mtx_plain);
    thrd_t projectileCalculator;
    thrd_create(&projectileCalculator,calcProjectiles, nullptr);
    for (int i = 0; i < 4; ++i) {
        if (IsClientOnline(i)) {
            char buffer[4][64]; //注：此处GetTick获得的是房主的时间戳
            BroadcastPlayerPositionUpdatePackToPlayer(ServerPlayers[i].id, GetTick(), ServerPlayers[i].x,
                                                      ServerPlayers[i].y, ServerPlayers[i].direction, buffer[i]);
        }
    }
}

void ServerStopGame()
{
    mtx_destroy(&ProjectileCalcLocker);
    for (int i = 0; i < 4; ++i) {
        Projectiles[i].clear();
    }
}

void ExecutePlayerPackage(GamePack pack,char *buffer) {
    int projectileId;
    switch (pack.packId) {
        case PLAYER_POSITION_UPDATE:
            handlePlayerPositionUpdatePack(pack,
                                           (pack.data[1]&0x0f) * 100 + (pack.data[2]&0x0f) * 10 + (pack.data[3]&0x0f),
                                           (pack.data[4]&0x0f) * 100 + (pack.data[5]&0x0f) * 10 + (pack.data[6]&0x0f),
                                           pack.data[0]&0x0f,buffer);
            break;
        case PLAYER_ACCUMULATE_POWER:
            ServerPlayers[pack.playerId].accumulateTime = pack.data[0] & 0x0f;
            BroadcastPlayerAccumulatePowerPackToPlayer(pack.playerId,pack.tick,ServerPlayers[pack.playerId].accumulateTime,buffer);
            break;
        case PROJECTILE_SHOOT:
            projectileId = (pack.data[0]&0x0f)*1000 + (pack.data[1]&0x0f)*100 + (pack.data[2]&0x0f)*10 + (pack.data[3]&0x0f);
            int leftAmmunition = ServerPlayers[pack.playerId].ammunition - GetProjectileConsume(pack.data[11]&0x0f);
            if (leftAmmunition >= 0) {
                mtx_lock(&ProjectileCalcLocker);
                Projectiles[pack.playerId][projectileId] = {pack.data[11]&0x0f, GetProjectileDurability(pack.data[11]&0x0f),(pack.data[4]&0x0f) * 100 + (pack.data[5]&0x0f) * 10 + (pack.data[6]&0x0f),(pack.data[7]&0x0f) * 100 + (pack.data[8]&0x0f) * 10 + (pack.data[9]&0x0f),pack.data[10]&0x0f,pack.playerId,pack.tick};
                mtx_unlock(&ProjectileCalcLocker);
                if (!IsPlayerInBase(map,ServerPlayers,pack.playerId)) {
                    ServerPlayers[pack.playerId].ammunition = leftAmmunition;
                    BroadcastPlayerAmmunitionUpdatePackToPlayer(pack.playerId,pack.tick,ServerPlayers[pack.playerId].ammunition,buffer);
                }
                BroadcastProjectileShootPackToPlayer(pack.playerId,projectileId,(pack.data[4]&0x0f) * 100 + (pack.data[5]&0x0f) * 10 + (pack.data[6]&0x0f),(pack.data[7]&0x0f) * 100 + (pack.data[8]&0x0f) * 10 + (pack.data[9]&0x0f),pack.data[10]&0x0f,pack.data[11]&0x0f,pack.tick,buffer);
                ServerPlayers[pack.playerId].accumulateTime = 0;
                BroadcastPlayerAccumulatePowerPackToPlayer(pack.playerId,pack.tick,0,buffer);
            }//否则 已读不回
            break;
    }
}

void handlePlayerPositionUpdatePack(GamePack &pack,int x,int y,int direction,char* buffer) {
    if (!map.contains(x,y)) {
        printf("出界：%d %d恢复到: %d %d\n",x,y,ServerPlayers[pack.playerId].x,ServerPlayers[pack.playerId].y);
        ServerPlayers[pack.playerId].direction = direction;
        if (x < map.getArea().left) {
            ServerPlayers[pack.playerId].x = map.getArea().left;
        }
        if (x > map.getArea().right) {
            ServerPlayers[pack.playerId].x = map.getArea().right;
        }
        if (y < map.getArea().top) {
            ServerPlayers[pack.playerId].y = map.getArea().top;
        }
        if (y > map.getArea().bottom) {
            ServerPlayers[pack.playerId].y = map.getArea().bottom;
        }
        BroadcastPlayerPositionUpdatePackToPlayer(pack.playerId,pack.tick,ServerPlayers[pack.playerId].x,ServerPlayers[pack.playerId].y,ServerPlayers[pack.playerId].direction,buffer);
        return;
    }
    bool b1,b2,b3,b4;
    switch (direction) {
        case 0:
            b1 = map.getBlockLevelByPixel(x+40,y);
            b2 = map.getBlockLevelByPixel(x+40,y+9);
            b3 = map.getBlockLevelByPixel(x+40,y+31);
            b4 = map.getBlockLevelByPixel(x+40,y+40);
            if (b2 || b3) {
                x = (x-map.getArea().left) /40 * 40 + map.getArea().left;
                y = ServerPlayers[pack.playerId].y;
                //printf("左侧有障碍，无法通行");
                break;
            }
            if (b1 && !b4) { //往下偏移
                y = (y-map.getArea().top) / 40 * 40 + 40 + map.getArea().top;
                //printf("左侧有障碍，向下偏移");
            }
            if (!b1 && b4) { //往上偏移
                y = (y-map.getArea().top) / 40 * 40 + map.getArea().top;
                //printf("左侧有障碍，向上偏移");
            }
            break;
        case 1:
            b1 = map.getBlockLevelByPixel(x,y);
            b2 = map.getBlockLevelByPixel(x+9,y);
            b3 = map.getBlockLevelByPixel(x+31,y);
            b4 = map.getBlockLevelByPixel(x+40,y);
            if (b2 || b3) {
                x = ServerPlayers[pack.playerId].x;
                y = (y-map.getArea().top) / 40 * 40 + 40 + map.getArea().top;
                break;
            }
            if (b1 && !b4) { //往右偏移
                x = (x-map.getArea().left) / 40 * 40 + 40 + map.getArea().left;
            }
            if (!b1 && b4) { //往左偏移
                x = (x-map.getArea().left) / 40 * 40 + map.getArea().left;
            }
            break;
        case 2:
            b1 = map.getBlockLevelByPixel(x,y);
            b2 = map.getBlockLevelByPixel(x,y+9);
            b3 = map.getBlockLevelByPixel(x,y+31);
            b4 = map.getBlockLevelByPixel(x,y+40);
            if (b2 || b3) {
                x = (x-map.getArea().left) / 40 * 40 + 40 + map.getArea().left;
                y = ServerPlayers[pack.playerId].y;
                break;
            }
            if (b1 && !b4) { //往下偏移
                y = (y-map.getArea().top) / 40 * 40 + 40 + map.getArea().top;
                //printf("右侧有障碍，向下偏移");
            }
            if (!b1 && b4) { //往上偏移
                y = (y-map.getArea().top) / 40 * 40 + map.getArea().top;
            }
            break;
        case 3:
            b1 = map.getBlockLevelByPixel(x,y+40);
            b2 = map.getBlockLevelByPixel(x+9,y+40);
            b3 = map.getBlockLevelByPixel(x+31,y+40);
            b4 = map.getBlockLevelByPixel(x+40,y+40);
            if (b2 || b3) {
                x = ServerPlayers[pack.playerId].x;
                y = (y-map.getArea().top) / 40 * 40 + map.getArea().top;
                break;
            }
            if (b1 && !b4) { //往右偏移
                x = (x-map.getArea().left) / 40 * 40 + 40 + map.getArea().left;
            }
            if (!b1 && b4) { //往左偏移
                x = (x-map.getArea().left) / 40 * 40 + map.getArea().left;
            }
            break;
    }
    ServerPlayers[pack.playerId].x = x;
    ServerPlayers[pack.playerId].y = y;
    ServerPlayers[pack.playerId].direction = direction;
    if (IsPlayerInBase(map,ServerPlayers,pack.playerId) && ServerPlayers[pack.playerId].ammunition < 5) {
        ServerPlayers[pack.playerId].ammunition = 5;
        BroadcastPlayerAmmunitionUpdatePackToPlayer(pack.playerId,pack.tick,5,buffer);
    }
    BroadcastPlayerPositionUpdatePackToPlayer(pack.playerId,pack.tick,x,y,direction,buffer);
}
#include <tool.h>
int calcProjectiles(void* arg) {
    char buffer[128];
    while(ServerOn) {
        for (int i = 0; i < 4; ++i) {
            for (auto it = Projectiles[i].begin(); it != Projectiles[i].end();) {
                auto nextIt = std::next(it);
                for (int j = 0; j < GetProjectileSpeed(it->second.level); ++j) {
                    it->second.x += GetMoveX(it->second.direction) * 10;
                    it->second.y += GetMoveY(it->second.direction) * 10;
                    it->second.shootTick++;
                    //判断炮弹有没有出范围
                    if (!map.contains(it->second.x,it->second.y)) {
                        mtx_lock(&ProjectileCalcLocker);
                        Projectiles[i].erase(it);
                        mtx_unlock(&ProjectileCalcLocker);
                        break;
                    }
                    int blockLevel = map.getBlockLevelByPixel(it->second.x,it->second.y);
                    if (blockLevel > 0) {
                        if (blockLevel == 9) {
                            BroadcastProjectileDurabilityUpdatePackToPlayer(it->second.shooter,it->first,it->second.shootTick,0,buffer);
                            mtx_lock(&ProjectileCalcLocker);
                            Projectiles[i].erase(it);
                            mtx_unlock(&ProjectileCalcLocker);
                            break;
                        }
                        Position pos = map.getBlockPositionByPixel(it->second.x,it->second.y);
                        int leftBlockLevel = blockLevel - it->second.health;
                        if (blockLevel < 5 && it->second.level == 4) { //子弹不受伤，摧毁方块
                            map.setBlockLevelByPosition(pos.row,pos.col,0);
                            //printf("tick=%d 炮弹击中x=%d y=%d col=%d row=%d\n",it->second.shootTick,it->second.x,it->second.y,pos.col,pos.row);
                            BroadcastBlockHealthUpdatePackToPlayer(it->second.shooter,it->second.shootTick,pos.col,pos.row,0,buffer);
                            continue;
                        }
                        if (leftBlockLevel <= 0) {
                            leftBlockLevel = 0;
                        }
                        map.setBlockLevelByPosition(pos.row,pos.col,leftBlockLevel);
                        //printf("tick=%d 炮弹击中x=%d y=%d col=%d row=%d\n",it->second.shootTick,it->second.x,it->second.y,pos.col,pos.row);
                        BroadcastBlockHealthUpdatePackToPlayer(it->second.shooter,it->second.shootTick,pos.col,pos.row,leftBlockLevel,buffer);
                        int leftDurability = it->second.health - blockLevel;
                        if (leftDurability <= 0) {
                            leftDurability = 0;
                        }
                        it->second.health = leftDurability;
                        BroadcastProjectileDurabilityUpdatePackToPlayer(it->second.shooter,it->first,it->second.shootTick,leftDurability,buffer);
                        if (leftDurability == 0) {
                            mtx_lock(&ProjectileCalcLocker);
                            Projectiles[i].erase(it);
                            mtx_unlock(&ProjectileCalcLocker);
                            break;
                        }
                    }
                    int crashPlayer = GetLocalPlayerIdByXY(ServerPlayers, it->second.x, it->second.y);
                    if (crashPlayer != -1 && crashPlayer != it->second.shooter) {
                        ServerPlayers[crashPlayer].health -= GetProjectileDamage(it->second.level);
                        if (ServerPlayers[crashPlayer].health <= 0) {
                            PixelXY base = GetPlayerBaseXY(map,crashPlayer);
                            ServerPlayers[crashPlayer].health = 5;
                            ServerPlayers[crashPlayer].x = base.x;
                            ServerPlayers[crashPlayer].y = base.y;
                            BroadcastPlayerPositionForceUpdatePackToPlayer(crashPlayer,it->second.shootTick,base.x,base.y,ServerPlayers[crashPlayer].direction,buffer);
                        }
                        BroadcastProjectileDurabilityUpdatePackToPlayer(it->second.shooter,it->first,it->second.shootTick,0,buffer);
                        BroadcastPlayerHealthUpdatePackToPlayer(crashPlayer,it->second.shootTick,ServerPlayers[crashPlayer].health,buffer);
                        mtx_lock(&ProjectileCalcLocker);
                        Projectiles[i].erase(it);
                        mtx_unlock(&ProjectileCalcLocker);
                        break;
                    }
                }
                it = nextIt;
            }
        }
        Sleep(33);
        ProjectileTick++;
    }
    return 0;
}